package com.tiancheng.trade.payment.util;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import jakarta.validation.constraints.NotNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;

/**
 * sftp工具类
 *
 * @author DCH
 * @date 2024/11/06
 */
@Slf4j
public final class SftpUtil {
    /**
     * 公钥检查机制
     */
    public static final String HOST_KEY_CHECKING_NO = "no";
    public static final String HOST_KEY_CHECKING_ASK = "ask";
    public static final String HOST_KEY_CHECKING_YES = "yes";
    /**
     * 连接模式
     */
    public static final String CON_MODE_SHELL = "shell";
    public static final String CON_MODE_EXEC = "exec";
    public static final String CON_MODE_SFTP = "sftp";
    /**
     * linux目录权限
     */
    public static final String PATH_VALID_775 = "775";

    /**
     * 打开服务器通道连接
     *
     * @param identityFilePath 密钥路径
     * @param userName         用户名
     * @param password         密码
     * @param host             主机地址
     * @param port             端口
     * @param hostKeyChecking  公钥检查机制：no、ask、yes
     * @param conMode          连接模式：shell、exec、sftp...
     * @return Channel
     */
    public static Channel getChannel(String identityFilePath,
                                     @NotNull String userName,
                                     @NotNull String password,
                                     @NotNull String host,
                                     @NotNull Integer port,
                                     String hostKeyChecking,
                                     String conMode) throws Exception {

        JSch jsch = new JSch();
        if (!StringUtils.isBlank(identityFilePath)) {
            jsch.addIdentity(identityFilePath);
        }
        Session session = jsch.getSession(userName, host, port);
        session.setPassword(password);
        if (!StringUtils.isBlank(hostKeyChecking)) {
            session.setConfig("StrictHostKeyChecking", hostKeyChecking);
        }
        session.connect();
        Channel channel = session.openChannel(conMode);
        channel.connect();
        return channel;
    }

    /**
     * 文件下载
     *
     * @param targetDir  目标文件目录，例：/home/user、user、user/
     * @param targetFile 目标文件名
     * @param saveFile   本地保存目录及文件名，例：/home/user/test.txt
     * @param sftp       连接
     */
    public static void download(@NotNull String targetDir,
                                @NotNull String targetFile,
                                @NotNull String saveFile,
                                @NotNull ChannelSftp sftp) {
        try {
            if (!isDirExist(targetDir, sftp)) {
                log.error("file: " + targetFile + " download error: directory \"" + targetDir + "\" is not exist");
                return;
            }
            sftp.cd(targetDir);
            File file = new File(saveFile);
            if (!file.exists()) {
                String dir = saveFile.substring(0, saveFile.lastIndexOf("/"));
                File parentDir = new File(dir);
                if (!parentDir.exists()) {
                    parentDir.mkdirs();
                }
                file.createNewFile();
            }
            sftp.get(targetFile, new FileOutputStream(file));
        } catch (Exception e) {
            log.error("file: " + targetFile + " download error: " + e);
        }
    }

    /**
     * 上传文件
     *
     * @param path      目标目录
     * @param sftp      服务器连接
     * @param pathValid 目录权限
     * @param files     待上传文件
     * @return boolean
     */
    public static boolean upload(@NotNull String path,
                                 @NotNull ChannelSftp sftp,
                                 @NotNull String pathValid,
                                 @NotNull File... files) {
        StringBuilder fileNames = new StringBuilder();
        try {
            String[] pathArr = path.split("/");
            for (String pathNode : pathArr) {
                if (StringUtils.isEmpty(pathNode)) {
                    continue;
                }
                // 判断每层目录
                if (!isDirExist(pathNode, sftp)) {
                    // 目录不存在则创建
                    sftp.mkdir(pathNode);
                    // 设置目录权限
                    sftp.chmod(Integer.parseInt(pathValid, 8), pathNode);
                }
                sftp.cd(pathNode);
            }
            // 上传文件
            for (File file : files) {
                fileNames.append(file.getName()).append(", ");
                sftp.put(new FileInputStream(file), file.getName());
            }
            return true;
        } catch (Exception e) {
            log.error("files: " + fileNames.toString() + " upload error: " + e.getMessage());
        }
        return false;
    }

    /**
     * 判断目录是否存在，可判断目录样例：/home/user、user、user/
     *
     * @param path 需要判断的目录
     * @param sftp 连接
     * @return boolean
     */
    public static boolean isDirExist(@NotNull String path, @NotNull ChannelSftp sftp) {
        try {
            SftpATTRS sftpATTRS = sftp.lstat(path);
            return sftpATTRS.isDir();
        } catch (Exception ignored) {
            return false;
        }
    }

    /**
     * 获取目录下所有子文件夹
     *
     * @param path 需要判断的目录
     * @param sftp 连接
     * @return {@link List<ChannelSftp.LsEntry>}
     */
    public static List<ChannelSftp.LsEntry> getDirs(@NotNull String path, @NotNull ChannelSftp sftp) {
        List<ChannelSftp.LsEntry> filesAndDirsByDir = getFilesAndDirsByDir(path, sftp);
        if (!CollectionUtils.isEmpty(filesAndDirsByDir)) {
            return filesAndDirsByDir.stream().filter(lsEntry -> lsEntry.getAttrs().isDir()).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 获取所有文件夹
     *
     * @param dir  需要判断的目录
     * @param sftp 连接
     * @return {@link List<ChannelSftp.LsEntry>}
     */
    public static List<ChannelSftp.LsEntry> getFilesByDir(@NotNull String dir, @NotNull ChannelSftp sftp) {
        List<ChannelSftp.LsEntry> filesAndDirsByDir = getFilesAndDirsByDir(dir, sftp);
        if (!CollectionUtils.isEmpty(filesAndDirsByDir)) {
            return filesAndDirsByDir.stream().filter(lsEntry -> !lsEntry.getAttrs().isDir()).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 获取目录下所有子文件夹
     *
     * @param dir  父文件夹目录
     * @param sftp 连接
     * @return {@link List<ChannelSftp.LsEntry>}
     */
    public static List<ChannelSftp.LsEntry> getFilesAndDirsByDir(@NotNull String dir, @NotNull ChannelSftp sftp) {
        List<ChannelSftp.LsEntry> lsEntries = new ArrayList<>();
        try {
            if (isDirExist(dir, sftp)) {
                Vector ls = sftp.ls(dir);
                for (Object l : ls) {
                    ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) l;
                    if (entry.getFilename().startsWith(".")) {
                        continue;
                    }
                    lsEntries.add(entry);
                }
            }
        } catch (Exception ignored) {
            return null;
        }
        return lsEntries;
    }
}